using UnityEngine;
using LS.GameKit;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using UnityEngine.Tilemaps;
using UnityEngine.UI;

namespace LS.Samples
{
    public class MapBlockView2DSample : MonoBehaviour
    {
        [SerializeField] private Camera m_MainCamera;
        [SerializeField] private MapBlockViewer2D m_MapBlockView;
        [SerializeField] private Tilemap m_TileMapPlaneLayer;
        [SerializeField] private Tilemap m_TileMapWallLayer;
        [SerializeField] private GameObject m_SketchTogglePrefab;
        [SerializeField] private RectTransform m_SketchViewContent;
        [SerializeField] private Unit2DManager m_Unit2DManager;
        [SerializeField] private List<SketchMapContent> m_SketchMaps = new List<SketchMapContent>();

        private int m_Index = -1;
        private bool m_IsPlacing = false;
        private ToggleGroup m_ToggleGroup = null;
        private Plane m_Plane = new Plane(Vector3.back, Vector3.zero);
        private readonly List<Vector2Int> m_TempObstacleList = new List<Vector2Int>();

        public bool IsPlacing
        {
            get { return m_IsPlacing; }
        }

        private void Awake()
        {
            m_ToggleGroup = m_SketchViewContent.GetComponent<ToggleGroup>();
        }

        private void Start()
        {
            for (int i = 0; i < m_SketchMaps.Count; i++)
            {
                int index = i;
                var sm = m_SketchMaps[i];
                GameObject toggleObj = GameObject.Instantiate(m_SketchTogglePrefab, m_SketchViewContent);
                toggleObj.SetActive(true);

                Toggle toggle = toggleObj.GetComponent<Toggle>();
                toggle.group = m_ToggleGroup;
                toggle.onValueChanged.AddListener(isOn =>
                {
                    if (isOn) PlaceStart(index);
                });

                Image img = toggle.targetGraphic as Image;
                img.sprite = sm.sprite;
            }

            m_ToggleGroup.SetAllTogglesOff(false);
        }

        private void Update()
        {
            if (m_IsPlacing)
            {
                if (QI.MouseRightDown())
                {
                    //Cancel.
                    PlaceEnd();
                    return;
                }

                Vector2 inputPoint = QI.MousePosition();
                Vector3 screenPoint = new Vector3(inputPoint.x, inputPoint.y, m_MainCamera.nearClipPlane);
                Ray ray = m_MainCamera.ScreenPointToRay(screenPoint);
                if (m_Plane.Raycast(ray, out float enter))
                {
                    Vector3 position = ray.GetPoint(enter);
                    Vector2 size = m_SketchMaps[m_Index].size;
                    Sprite sprite = m_SketchMaps[m_Index].sprite;
                    bool placeClean = ViewMapBlockClean(position, size, sprite);
                    if (QI.MouseLeftDown() && !EventSystem.current.IsPointerOverGameObject() && placeClean)
                    {
                        //PutDown
                        Bounds bound = new Bounds(position, size);
                        m_Unit2DManager.CreateUnit(bound, sprite);
                        PlaceEnd();
                    }
                }
            }
        }

        public void PlaceStart(int index)
        {
            m_Index = Mathf.Clamp(index, 0, m_SketchMaps.Count - 1);
            m_IsPlacing = true;
        }

        public void PlaceEnd()
        {
            m_IsPlacing = false;
            m_Index = -1;
            m_ToggleGroup.SetAllTogglesOff();
            m_MapBlockView.Hide();
        }

        public bool ViewMapBlockClean(Vector3 position,Vector2 size, Sprite sprite)
        {
            Vector2 pivot =  sprite.pivot / sprite.rect.size;
            Vector3 finalPosition = position - (Vector3)(size * pivot);
            Vector2 finalSize = size;
            bool[,] testResult;
            Color[] colors;
            Rect targetRect = new Rect(finalPosition, finalSize);
            bool blockClean = !ObstacleTest(targetRect, out Vector2Int[] _, out testResult);

            int xCount = testResult.GetLength(0);
            int yCount = testResult.GetLength(1);
            
            colors = new Color[xCount * yCount];
            for (int y = 0; y < yCount; y++)
            {
                for (int x = 0; x < xCount; x++)
                {
                    colors[xCount * y + x] = testResult[x, y] ? Color.red : Color.green;
                }
            }

            m_MapBlockView.Show(position, size, sprite, colors);
            return blockClean;
        }

        public bool ObstacleTest(Rect rect, out Vector2Int[] obstaclePosition,
            out bool[,] testResult, bool testMapBlock = true, bool testUnit = true)
        {
            m_TempObstacleList.Clear();
            Vector2Int size = Vector2Int.RoundToInt(rect.size);
            Vector2Int position = Vector2Int.RoundToInt(rect.min);

            testResult = new bool[size.x, size.y];
            
            if (testMapBlock)
            {
                for (int y = 0; y < size.y; y++)
                {
                    for (int x = 0; x < size.x; x++)
                    {
                        Vector2Int tileBlockPosition = position + new Vector2Int(x, y);
                        if (m_TileMapWallLayer.GetTile((Vector3Int)tileBlockPosition) ||
                            !m_TileMapPlaneLayer.GetTile((Vector3Int)tileBlockPosition))
                        {
                            m_TempObstacleList.Add(tileBlockPosition);
                            testResult[x, y] = true;
                        }
                        else
                        {
                            testResult[x, y] = false;
                        }
                    }
                }
            }

            if (testUnit)
            {
                if (m_Unit2DManager.CastUnit(rect, out Vector2Int[] unitPositions,
                        out bool[,] unitTestResult))
                {
                    for (int i = 0; i < unitPositions.Length; i++)
                    {
                        if (!m_TempObstacleList.Contains(unitPositions[i]))
                        {
                            m_TempObstacleList.Add(unitPositions[i]);
                        }
                    }

                    for (int y = 0; y < unitTestResult.GetLength(1); y++)
                    {
                        for (int x = 0; x < unitTestResult.GetLength(0); x++)
                        {
                            testResult[x, y] = unitTestResult[x, y] || testResult[x, y];
                        }
                    }
                }
            }

            obstaclePosition = m_TempObstacleList.ToArray();
            return obstaclePosition.Length > 0;
        }

        [System.Serializable]
        public class SketchMapContent
        {
            public Sprite sprite;
            public Vector2 size;
        }
    }
}